/*******************************************************************************
 * This file is part of blueprint4j www.blueprint4j.org.
 * 
 *     blueprint4j is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU Lesser General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 * 
 *     blueprint4j is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU Lesser General Public License for more details.
 * 
 *     You should have received a copy of the GNU Lesser General Public License
 *     along with blueprint4j. If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/
package blueprint4j.comm.io;

import blueprint4j.utils.*;

import java.io.*;

public abstract class LineReader {
	private int sotimeout = 60000;
	private LineReader reader = null;
	private int mark_no_read = 0;
	private int mark_last_available = 0;
	private InputStream stream = null;
	
	// EMPTY CONSTRUCTIR
	protected LineReader() {}
	
	public LineReader(InputStream instream) {
		this.reader = new LineReaderRead(instream);
		this.stream = instream;
	}
	
	public LineReader(LineReader reader) {
		this.reader = reader;
	}
	
	/**
	 * When a different LineReader have been working with this stream
	 * You should copy the unread data accorss
	 */
	public void setReference(LineReader reader) throws IOException {
		if (reader.getUnread() != null) {
			unread(reader.getUnread());
		}
	}
	
	public byte[] getUnread() {return reader.getUnread();}

	/**
	 * -1 will never timeout
	 * -2 will not wait but will not throw a timeout exception
	 */
	public void setSoTimeout(int sotimeout) {
		this.sotimeout = sotimeout;
	}

	public int getSoTimeout() {
		return sotimeout;
	}
	
	public void close() throws IOException {
		reader.close();
	}
	
	protected InputStream getInputStream() {
		if (stream == null) {
			return reader.getInputStream();
		} else {
			return stream;
		}
	}

	/**
	 * Are there any bytes avbailable on the stream
	 * otherwise do not waste your time
	 */
	protected boolean available() throws IOException {
		return reader.available();
	}
	
	protected int availableInTopStream() throws IOException {
		return reader.availableInTopStream();
	}
	
	protected void markNoRead() {
		mark_no_read = mark_last_available;
	}
	
	protected void unread(byte data[]) throws IOException {
		reader.unread(data);
	}
	
	protected void unread(byte data[],int start,int end) throws IOException {
		reader.unread(data,start,end);
	}

	protected byte[] readBreak() throws IOException,TimeoutException {
		int cnt= 0;
		byte read [] = null;
		for (
		   read = readByteFromStream();
		   (read == null && sotimeout != -2);
		   read = readByteFromStream()
		) {
			try {
				cnt++;
				Thread.sleep(100);
			} catch (InterruptedException ie) {}
			if (sotimeout >= 0 && cnt * 100 > sotimeout) {
				throw new TimeoutException("Timed out reading from stream");
			}
		}
		return read;
	}
	
	protected byte[] read() throws IOException,TimeoutException {
		return reader.readBreak();
	}

	public byte[] readLine() throws IOException,TimeoutException {
		byte read[] = readBreak();
		if (read !=null && read.length == 0) {
			read = null;
		}
		return read;
	}
	
	public String readLineStr() throws IOException,TimeoutException {
		byte read[] = readLine();
		if (read == null) {
			return null;
		} else {
			return new String(read);
		}
	}

	private byte [] readByteFromStream() throws IOException,TimeoutException {
		if (availableInTopStream() == mark_no_read) {
			return null;
		}
		mark_no_read = -1;
		if (available()) {
			mark_last_available = availableInTopStream();
			byte data [] = readBytes();
			if (data != null) {
				mark_no_read = -1;
			}
			return data;
		} else {
			return null;
		}
	}
	abstract byte [] readBytes() throws IOException,TimeoutException ;
}
